//
// Created by 11510 on 2024/7/11.
//

#ifndef REACTOR_V5_TIMERFD_H
#define REACTOR_V5_TIMERFD_H
#include <unistd.h>
#include <poll.h>
#include <sys/timerfd.h>
#include <functional>

class TimerFd {
private:
    using TimerfdCallback = std::function<void()>;
    int _timerfd;
    TimerfdCallback _cb;
    bool _isStarted;
    int _initSec;
    int _periodSec;

public:
    TimerFd(TimerfdCallback &&cb, int sec, int period) :
            _timerfd(createTimerfd()),
            _cb(std::move(cb)),
            _isStarted(false),
            _initSec(sec),
            _periodSec(period) {}

    ~TimerFd(){
        setTimerfd(0,0);
        close(_timerfd);
    }

    void start(){
        struct pollfd pfd;
        pfd.fd = _timerfd;
        pfd.events = POLLIN;

        setTimerfd(_initSec, _periodSec);

        _isStarted = true;
        while (_isStarted) {
            int nready = poll(&pfd, 1, 5000);
            if (-1 == nready && errno == EINTR) {
                continue;
            } else if (-1 == nready) {
                perror("-1 == nready");
            } else if (0 == nready) {
                perror("0 == nready");
            } else {
                if (pfd.revents & POLLIN) {
                    handleRead();
                    if (_cb) {
                        _cb();
                    }
                }

            }

        }
    }

    void stop(){
        _isStarted = false;
    }

    int createTimerfd(){
        int fd = timerfd_create(CLOCK_REALTIME, 0);
        if (fd < 0) {
            perror("eventfd");
            return fd;
        }
        return fd;
    }

    void handleRead(){
        uint64_t one;
        ssize_t ret = read(_timerfd, &one, sizeof one);
        if (ret != sizeof one) {
            perror("read");
            return;
        }
    }

    void setTimerfd(int sec, int period){
        struct itimerspec newValue;
        newValue.it_value.tv_sec = sec;
        newValue.it_value.tv_nsec = 0;

        newValue.it_interval.tv_sec = period;
        newValue.it_interval.tv_nsec = 0;

        int ret = timerfd_settime(_timerfd, 0, &newValue, NULL);
        if(ret < 0){
            perror("setTimerfd");
        }
    }

};


#endif //REACTOR_V5_TIMERFD_H
